// ----------------------------------------
// Filename: Ready_Queue.c
// Description: The code that implements a ready queue
// Author: Jon Maloney
// Date: 30-May-2012
//

//************************************************************
//                    Include .h files
//************************************************************
#include "Ready_Queue.h"
#include <stdlib.h>
//************************************************************
//                    Include external references
//************************************************************

//************************************************************
//                    Define Statements
//************************************************************

//************************************************************
//                    Struct, Union, enum Statements
//************************************************************

//************************************************************
//                    Global variables and constants
//************************************************************

//************************************************************
//                    Prototype Private Functions
//************************************************************

//************************************************************
//                    Implementation
//************************************************************

// ----------------------------------------
// ReadyQueue_Create
// 
// Creates a new ready queue
//	
// Input:
// 	rQueue: 	The ready queue that the element is to be inserted into.
// Output:
//  pdTRUE: 	if the ready queue could be initialised
//	pdFALSE:	if there was not enough memory to create the ready queue
// Conditions:
// 	none
//
rQueue_Handle ReadyQueue_Create( void )
{
	T_READY_QUEUE * rQueue;
	UINT8 i;
	//Allocates some heap memory to the ready queue
	rQueue = (T_READY_QUEUE *)malloc(sizeof(T_READY_QUEUE));

	//Test to see if malloc was successful.
	if(rQueue == NULL)
		return NULL;

	//Make sure that each of the array elements are NULL
	for(i = 0; i< configNB_PRIORITY_LEVELS; i++)
		rQueue->rQueues[i] = NULL;
	
	rQueue->higestPriorityElementInQueue = 0;

	return (rQueue_Handle)rQueue;
}

// ----------------------------------------
// ReadyQueue_Add
// 
// Adds an element to the ready queue.
//	
// Input:
// 	rQueue: 	The ready queue that the element is to be inserted into.
//	element:	The element that is to be inserted
//	priority:	The priority of the element being inserted into the ready queue
// Output:
//  a list handle to the node just created.
// Conditions:
// 	none
//
linkedList_Handle ReadyQueue_Add( rQueue_Handle rQueue, linkedList_Handle node, void * item, PRIORITY priority )
{
	node = LinkedList_Add(rQueue->rQueues[priority], item);

	if(node == NULL)
		return NULL;
	
	rQueue->rQueues[priority] = node;

	if(rQueue->higestPriorityElementInQueue < priority)
	{
		rQueue->higestPriorityElementInQueue = priority;
		return NULL;	//A priority change has occured
	}
	
	return node;	//No priority change occured
}

// ----------------------------------------
// ReadyQueue_AddNode
// 
// Adds node to the ready queue.
//	
// Input:
// 	rQueue: 	The ready queue that the node is to be inserted into.
//	node:		The node to be inserted.
//	priority:	The priority of the element being inserted into the ready queue
// Output:
//  pdTRUE: 	If there has been a change in the queue's priority (A higher priority element has been added to the queue)
//	pdFALSE:	If no change has occured in the queue's priority. 
// Conditions:
// 	none
//
BOOL ReadyQueue_AddNode( rQueue_Handle rQueue, linkedList_Handle node, PRIORITY priority )
{
	rQueue->rQueues[priority] = LinkedList_Insert(rQueue->rQueues[priority], node);

	if(rQueue->higestPriorityElementInQueue < priority)
	{
		rQueue->higestPriorityElementInQueue = priority;
		return pdTRUE;	//A priority change has occured
	}
	
	return pdTRUE;	//No priority change occured
}

// ----------------------------------------
// ReadyQueue_Remove
// 
// Removes an element from the ready queue.
//	This does not delete the element only removes the reference
//	to the element from the ready queue.
//	
// Input:
// 	rQueue: 	The ready queue that the element is to be removed from.
//	element:	The element that is to be removed
//	priority:	The priority of the element being removed from the the ready queue
// Output:
//  pdTRUE: 	If there has been a change in the queue's priority.
//	pdFALSE:	If no change has occured in the queue's priority. 
// Conditions:
// 	none
//
inline BOOL ReadyQueue_Remove( rQueue_Handle rQueue, linkedList_Handle node, PRIORITY priority )
{
	//TCB * task;
	//Remove the element from the linked list
	if(LinkedList_Remove(node) == pdFALSE) //If there are no elements in the linked list
	{
		rQueue->rQueues[priority] = NULL;//Set the array pointer to NULL
		
		if(rQueue->higestPriorityElementInQueue == priority && rQueue->higestPriorityElementInQueue != configIDLE_TASK_PRIORITY) //If this was the highest priority linked list in the queue
		{
			ENTER_CRITICAL();
			{
				//Update the highest priority element pointer
				do
				{
					rQueue->higestPriorityElementInQueue--;//Decrement the pointer
				//Until another linke list is found or we are at start of array.
				}while(rQueue->rQueues[rQueue->higestPriorityElementInQueue] == NULL && rQueue->higestPriorityElementInQueue != 0); //Zero must be the lowest priority item this is the idle task priority
			}
			EXIT_CRITICAL();
			return pdTRUE;	//Return true there has been a priority change in the queue
		}
	}
	else //If there are elements left in the queue
	{
		//Get the task that is left in the queue
		//	This is done incase we are removing a temporarily
		//	promoted thread.
		//task = (TCB *)node->NextNode->Item;
		
		//Update the ready queue
		rQueue->rQueues[priority] = (linkedList_Handle)node->NextNode;
	}
	return pdFALSE;//No priority change has occured
}

// ----------------------------------------
// ReadyQueue_Move
// 
// Moves an element in the ready queue from one priority level
//	to another.
//	
// Input:
// 	rQueue: 		The ready queue where the move is to take place.
//	element:		The element that is to be moved
//	FromPriority:	The current priority of the element being moved in the ready queue
// Output:
//  pdTRUE: 	If there has been a change in the queue's priority.
//	pdFALSE:	If no change has occured in the queue's priority. 
// Conditions:
// 	none
//
inline void ReadyQueue_Move( rQueue_Handle rQueue, linkedList_Handle node, UINT8 FromPriority,  UINT8 ToPriority )
{
	(void)ReadyQueue_Remove( rQueue, node, FromPriority );

	//returns true if the move caused the element to become the highest priority element in the queue
	(void) ReadyQueue_Add( rQueue, node, node->Item, ToPriority ); 
	
	return;
}

void * ReadyQueue_GetHighestPriorityItem( rQueue_Handle rQueue )
{
	if(rQueue->rQueues[rQueue->higestPriorityElementInQueue] == NULL)
		return NULL;
	
	return ((linkedList_Handle)rQueue->rQueues[rQueue->higestPriorityElementInQueue])->Item;
	
}

linkedList_Handle ReadyQueue_GetHighestPriorityNode( rQueue_Handle rQueue )
{
	if(rQueue->rQueues[rQueue->higestPriorityElementInQueue] == NULL)
		return NULL;
	
	return rQueue->rQueues[rQueue->higestPriorityElementInQueue];
	
}

void * ReadyQueue_GetAndRemoveHighestPriorityItem( rQueue_Handle rQueue )
{
	void * item;
	//Get the highest priority item in the ready queue
	item = ReadyQueue_GetHighestPriorityItem( rQueue );
	
	//Remove the item from the ready queue
	(void)ReadyQueue_Remove( rQueue, rQueue->rQueues[rQueue->higestPriorityElementInQueue], rQueue->higestPriorityElementInQueue );
	
	return item;
}

linkedList_Handle ReadyQueue_GetAndRemoveHighestPriorityNode( rQueue_Handle rQueue )
{
	//Get the highest priority node in the ready queue
	linkedList_Handle node = ReadyQueue_GetHighestPriorityNode( rQueue );
	
	//Remove the node from the ready queue
	(void)ReadyQueue_Remove( rQueue, node, rQueue->higestPriorityElementInQueue );
	
	return node;
}
